package monopd;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

// Created by Nikolas Wolfe, August 2, 2008

public class XMLServerListParser
{
	public String pathname;
	
	public DocumentBuilderFactory factory;
	
	public DocumentBuilder parser;
	
	public ArrayList<String[]> returnList;
	
	/* 	
	 *  the user of this class must ensure that XML docs used
	 *	meet this class' method specifications. 
	 *
	 *	EXAMPLE FORMAT (MUST CONFORM):
	 *
	 *	<?xml version='1.0'?>
	 *	<!-- Contact-Info: Jens Weibler <jens.weibler@jensthebrain.de> -->
	 *	<monopigator>
	 *	<server ip="64.135.210.4" host="doomopoly.doomworld.org" port="1234" version="0.9.3" users="0" />
	 *	<server ip="69.16.138.185" host="mst3000.org" port="1234" version="0.9.3" users="0" />
	 *	<server ip="69.221.138.43" host="niconet.otzo.com" port="12345" version="0.9.3" users="0" />
	 *	<server ip="78.47.209.170" host="monopd.drition.info" port="1234" version="0.9.3" users="0" />
	 *	<server ip="81.169.184.35" host="kermit.drition.info" port="1234" version="0.9.3" users="7" />
	 *	<server ip="75.150.48.221" host="atlantik.kevincroissant.com" port="1234" version="0.9.3" users="0" />
	 *	<server ip="66.148.74.40" host="20080802.catflap.bishopston.net" port="11234" version="0.9.3-p7" users="0" />
	 *	<server ip="72.234.163.180" host="monopd.hellwithit.info" port="8080" version="0.9.3" users="0" />
	 *	<server ip="208.116.11.40" host="20080802.catwalk.bishopston.net" port="11234" version="0.9.3" users="0" />
	 *	<server ip="69.13.181.249" host="20080802.catnip.bishopston.net" port="11234" version="0.9.3-p4" users="0" />
	 *	</monopigator>
	 *
	 */
	
	// constructor
	public XMLServerListParser( String pathname ) throws ParserConfigurationException
	{
		this.pathname = pathname;
		this.factory = DocumentBuilderFactory.newInstance();
		this.parser = factory.newDocumentBuilder();
		this.returnList = new ArrayList<String[]>();
	}
	
	/*
	 * this returns a list of servers as an arraylist of String arrays
	 * in the following format:
	 *
	 * [0] = hostname
	 * [1] = port
	 */ 
	public ArrayList<String[]> gimmeSomeServers()
	{
		// kick off the process
		try
		{
			getServerList();
		}
		catch( Exception e )
		{
			System.out.println( "You gave me a bad XML doc. Shame." );
			System.out.println(e);
			e.printStackTrace(System.err);
		}
		// return the array after it's been initialized 
		return this.returnList;
	}
	
	public void getServerList() throws SAXException,
			ParserConfigurationException, InstantiationException
	{
		// get xml form
		ReadFile rf = new ReadFile(new File(pathname));

		// create arraylist
		ArrayList<String> list = new ArrayList<String>();
		list = rf.readInput(list);

		// turn XML doc into useful array
		// after this point, ALL individual servers are in format:
		// <monopigator>
		// <server ip="69.16.138.185" host="mst3000.org" port="1234" version="0.9.3" users="0" />
		// </monopigator>
		list = makeLines(list);

		// loop here
		for (int i = 0; i < list.size(); i++)
		{
			// get parser
			try
			{
				Document doc = parser.parse(
						new ByteArrayInputStream(list.get(i).getBytes()));
				
				Element root = doc.getDocumentElement();

				// find children and turn them into a server list
				dispatchRoot(root);
			} 
			catch (IOException e)
			{
				System.out.println(e);
				e.printStackTrace(System.err);
			}
		}
	}

	public void dispatchRoot(Node node) throws SAXException,
			InstantiationException
	{
		checkNode(node, "monopigator");

		NodeList children = node.getChildNodes();
		for (int i = 0; i < children.getLength(); i++)
		{
			descendTree(children.item(i));
		}
	}
	
	public void descendTree( Node node ) throws SAXException, InstantiationException
	{
		// all items have the identification of "server"
		if( isNode( node, "server") )
		{
			retrieveServerAttributes( node );
		}
		else
		{
			// if we fail, pass up an exception
			throw new SAXException();
		}
	}
	
	public void retrieveServerAttributes( Node node ) throws SAXException
	{
		// get node map of XML attributes
		NamedNodeMap attributes = node.getAttributes();
		
		// get the attributes we care about
		String hostname = getString( attributes, "host" );
		String port = getString( attributes, "port" );
		
		// make return array
		String[] server = { hostname, port };
		
		// add this server to the list
		this.returnList.add( server );
	}
	
	// helper methods

	private void checkNode(Node node, String name) throws SAXException
	{
		if (!isNode(node, name))
			throw new SAXException("Expected " + name + " element");
	}

	private boolean isNode(Node node, String name)
	{
		return node != null && name.equals(node.getNodeName());
	}
	
	public String getString(NamedNodeMap attrs, String attName) 
		throws SAXException 
	{
		return getString(attrs, attName, false);
	}
	
	public String getString(NamedNodeMap attrs, String attName, boolean optional) 
		throws SAXException 
	{
		Node a = attrs.getNamedItem(attName);
		
		if( a == null )
			if( optional )
				return null;
			else
				throw new SAXException("Attribute " + attName + " not found");
		
		return a.getNodeValue();
	}

	public ArrayList<String> makeLines(ArrayList<String> list)
	{
		// return array
		ArrayList<String> retArr = new ArrayList<String>();

		// leading brackets start at 2 and end at the last
		// line of the xml doc
		String header = list.get(2);
		String footer = list.get(list.size() - 1);

		// get useful info
		for (int i = 3; i < list.size() - 1; i++)
		{
			String s = new String();
			s = header + list.get(i) + footer;
			retArr.add(s);
		}
		return retArr;
	}
}
